/**
 * @file   captura.cpp
 * @author Pedro Jimenez
 * @date   Wed Nov  5 16:33:38 2008
 * 
 * @brief  Contiene las funciones e hilos necesarios para capturar el video de las cámaras, grabar a fichero, mostrar por pantalla e imprimir la información en consola
 * 
 * 
 */
#define _CAPTURA_CPP_
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#include <sys/time.h>

#include "callbacks.h"

#include "interface.h"
#include "support.h"
#include "serial.h"
#include "../pic_code/PICdata.h"

#include "support.h"
#include "other_functions.h"

#include "frame_header.hh"

#include "interface_capture.h"
#include "captura.h"
#include "socket.h"
#include "shutter.h"
#include "face_detect_vj.hh"

void get_options(int argc,char *argv[], camera_control *cameras);
extern socket_params_t socket_params;

int qgrabar_camara;
/* Declaracion de una variable para utilizarla en la funcion cvWriteFrame */
//CvVideoWriter* writer;

pthread_mutex_t mutex_sync;
pthread_mutex_t mutex_show;
pthread_mutex_t mutex_print;

//************************************************************************
//                         f_capture_init
//************************************************************************
int f_capture_init(camera_control * cameras, CvRect roi, int argc, char **argv)
{
    /* Cargamos las opciones por defecto en la estructura */ 
    default_options(cameras); 

    if (dc_init(cameras) < 1) 
    {     
	/* Buscamos cámaras y les asignamos un nodo y un canal */
	return 0;
    }
    
    if (roi.x == -1)
	roi.x = MAX_RESOLUTION_WIDTH/2 - roi.width/2; 
    if (roi.y == -1)
	roi.y = MAX_RESOLUTION_HEIGHT/2 - roi.height/2; 
    if (!dc_config(cameras, roi.width, roi.height, roi.x, roi.y)) 
    {         
	/* Configuramos el buffer DMA y las cámaras para comenzar a tx */
	printf("\n\nERROR *************************************\n\n No se ha conectado la cámara.\n\n");
	return 0;
    }

    printf("Número de cámaras: %d\n",cameras[0].numCams);

    dc_init_images(cameras, 0);
    
    usleep(3400);

    dc_start_iso(cameras);

    pthread_mutex_init(&mutex_sync, NULL);
    pthread_mutex_init(&mutex_print, NULL);
    pthread_mutex_init(&mutex_show, NULL);

    for (int i=0; i<cameras[i].numCams; i++)
    {
	cameras[i].shutter_value = shutter_v;
	dc_set_feature(&cameras[i], 0, FEATURE_GAIN, (int)gain_value);
	dc_set_feature(&cameras[i], 0, FEATURE_SHUTTER, (int)cameras[i].shutter_value);
	dc_set_feature(&cameras[i], 0, FEATURE_BRIGHTNESS, (int)brightness_value);
    }

    
    return cameras[0].numCams;

}

//************************************************************************
//                         f_capture
//
// Hilo que captura de las cámaras y almacena la información en la
// estructura "cameras".
//************************************************************************


void * pthread_func_f_capture (void *parametro)
{
    captura_params_t *captura = (captura_params_t *)parametro;
    camera_control * camera = captura->camera;

    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

    usleep(250000);
    nice(0);

    double old_shutter_value = camera->shutter_value;
    camera->shutter_value_new = camera->shutter_value;

    while(1)
    {

	switch (external_sync[captura->cam])
	{
	case SYNC_EXTERNAL_ON:

	    pthread_mutex_lock(&mutex_sync);
	    if ( !dc_trigger_external_mode_on (camera, 1))
	    {
		pthread_mutex_unlock(&mutex_sync);
		fprintf(stderr, "Failed to start trigger dc1394 capture");
		return 0;
	    }
	    pthread_mutex_unlock(&mutex_sync);
	    external_sync[captura->cam] = SYNC_EXTERNAL_NULL;
	    break;
        
	case SYNC_EXTERNAL_OFF:

	    pthread_mutex_lock(&mutex_sync);
	    if ( !dc_trigger_external_mode_off (camera, 1))
	    {
		pthread_mutex_unlock(&mutex_sync);
		fprintf(stderr, "Failed to stop trigger dc1394 capture");
		return 0;
	    }
	    pthread_mutex_unlock(&mutex_sync);
	    external_sync[captura->cam] = SYNC_EXTERNAL_NULL;
	    break;

	default:
	    break;
	}

	//shutter control
	camera->shutter_value = old_shutter_value;
	if (camera->shutter_value_new != old_shutter_value)
	{
	    pthread_mutex_lock(&mutex_sync);
	    dc_set_feature(camera, 0, FEATURE_SHUTTER, (int)camera->shutter_value_new);
	    pthread_mutex_unlock(&mutex_sync);
	    old_shutter_value = camera->shutter_value_new;
	}

	// Captura de las cámaras.
	dc_capture(camera, 1); 

	if (!grabar_flag)
	{
	    camera->written_frame = 0;
	    iniciado_grabar = false;
	}
	else 
	{
	    if (grabar_frame(captura))
		camera->written_frame++;
	    iniciado_grabar = true;
	}

	pthread_mutex_lock(&mutex_print);
	captura->readframes++;
	captura->lastfilltime = camera->camera.filltime;
	pthread_mutex_unlock(&mutex_print);

    }
}



void * pthread_func_print_capture_info(void * parametro)
{
    //impresión del Frame Rate y buffer usado
    captura_params_t *captura = (captura_params_t *)parametro;
    int nnn=0;
    char avance[] = "-\\|/";
    float fps = 0.0;
    int buffers = 0;    
    int i, n = captura->camera->numCams;
    float capture_time[2];
    struct timeval oldfilltime[2];
    int nframes[2];
    int oldreadframes[2];
    //float diff =-1;

    nice(20);

    while (!captura[0].readframes)
	usleep(5000);

    // tiempo estimado para el frame rate, según el ancho de la imagen
    float capture_time_est = captura[0].camera->height * 0.0442 + 11.5250;
    printf ("\nFrame rate estimado: %6.2f pfs (%6.2f ms)\n", 1000 / capture_time_est, capture_time_est);

    while(1)
    {
	pthread_mutex_lock(&mutex_print);
	for (i=0; i<n; i++)
	{
	    capture_time[i] = difftime_ms(captura[i].lastfilltime, oldfilltime[i]);
	    oldfilltime[i] = captura[i].lastfilltime;
	    nframes[i] = captura[i].readframes - oldreadframes[i];
	    oldreadframes[i] = captura[i].readframes;
	}
	pthread_mutex_unlock(&mutex_print);

	if (!nframes[0])// || !nframes[1])
	{
	    usleep(30000);
	    continue;
	}

	nnn = (nnn+1) % 4;
	printf("\r");
	printf(" %c ", avance[nnn]);

	for (i=0; i<n; i++)
	{
	    capture_time[i] /= nframes[i];
	    if (capture_time[i] > capture_time_est*1.15  || capture_time[i] < capture_time_est/1.15)
	    {
		printf("\ncam %d -> %6.3f ms\n", i, capture_time[i]); 
		fflush(stdout);
	    }
	}
	fps = 1000.0/capture_time[0];
	printf("%4.1lf fps. ", fps);
	
	/*
	  if (n>=2 && oldreadframes[0] > 0)
	  {
	  diff = difftime_ms(oldfilltime[0], oldfilltime[1]);
	  diff += capture_time[0]*(oldreadframes[0] - oldreadframes[1]);
	  printf(" diff: %6.2f ms ", diff);

	  //printf(" c1: %d, c2 %d (%d)  ", oldreadframes[0], oldreadframes[1], 
	  //   oldreadframes[0] - oldreadframes[1]);
	  }
	*/

	if (grabar_flag)
	{
	    printf(" Frames: ");
	    printf(" %5d ", captura[0].camera->written_frame);
	    printf(" BUFFERS: ");
	    for (i=0; i<n; i++)
	    {
		buffers = captura[i].queue_img.size()*100/captura[i].buffer_img_size;
		printf("%3d%% ", buffers);
		if (buffers > 50 || buffers > 50)
		    printf("\n");
	    }
	    //printf(" grabando cam %d", qgrabar_camara);
	}
	
	printf("shutters: %4.0lf, %4.0f", captura[0].camera->shutter_value, captura[1].camera->shutter_value);
	fflush(stdout);
	usleep(300000);
    }
}



#define SIMULATE_WIDTH 1392
#define SIMULATE_HEIGHT 480

void * pthread_func_f_simulate (void * params)
{
    camera_control * cameras = (camera_control*)params;

    int x, y;
    unsigned char inicio=0;
    IplImage * img;

    img = cameras[0].image;

    while (1)
    {
	for (y=0; y<img->height; y++)
	{
	    for (x=0; x<img->width; x++)
	    {
		img->imageData[x + y*img->widthStep] = inicio + x+ y;
	    }
	}
	if (cameras[0].numCams>=2)
	{
	    //cvNot(img, cameras[1].image);
	    cvFlip(img, cameras[1].image);
	}
	
	inicio++;
	usleep(10000);
    }
}




//************************************************************************
//                         f_show_capture
//
// Captura la información de la cámara y la muestra en nuestra interface gráfica.
//************************************************************************

gboolean timeout_minimizar_show(gpointer data)
{
    //tras el timeout, vuelve a poner la ventana pequeña
    bool * p_data = (bool*)data;
    *p_data = false;

    //mata el timer
    return false;
}


/** 
 * Callback que se produce cuando un evento del mouse afecta a alguna de las dos ventanas de visualización
 * Cambia el tamaño de las ventanas de visualización al hacer doble-click con el mouse en alguna de ellas
 * @param event información sobre el botón pulsado en el mouse
 * @param x información sobre la posición del mouse
 * @param y información sobre la posición del mouse
 * @param flags información sobre el estado del mouse
 * @param param No recibe extra parámetros
 */
void onmouse (int event, int x, int y, int flags, void* param)
{
    //printf("evento %2d/%2d en %3d,%3d\n", event, flags, x, y);
    //fflush(stdout);

    if (event == 7 )
    {
	maximize_window       = !maximize_window && !esta_grabando;
	hold_window = false;
	if (maximize_window)
	{
	    g_timeout_add(3*60*1000 /*3 minutos*/, timeout_minimizar_show, (void*)&maximize_window);
	    //cvMoveWindow(SHOW_WINDOW[1], 200, 550);
	}
	else
	{
	    //cvMoveWindow(SHOW_WINDOW[1], 0, 200);
	}
	//cvDestroyWindow("Enfoque");
    }
    else if (event == 2)
    {
	hold_window =  !hold_window;
	if (hold_window)
	    g_timeout_add(1*60*1000 /*1 minutos*/, timeout_minimizar_show, (void*)&hold_window);
    }
}

void * pthread_func_f_show_capture (void* params)
{
    //struct timeval t1, t2;
    camera_control *cameras = (camera_control *)params;
    IplImage *img[2], *imgshow[2];
    void * window[2] = {0,0};
    int numCams = cameras[0].numCams;
    int i;
    bool maximize_window_old = maximize_window;
    bool max2 = false;
    int veces=0;
    HaarDetector haar;
    CvRect haarROI[2];
    double mean[2];
    init_gain_control(haarROI, 2, &haar);
    
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    
    for (i=0; i<numCams; i++)
	img[i] = cvCreateImage(cvSize(cameras[i].width/2, cameras[i].height/2), IPL_DEPTH_8U, 1);

    while(1)//!grabar_flag)
    {
	for (i=0; i<numCams; i++)
	{
	    //toma el puntero de la ventana para ver si se ha cerrado
	    if ((window[i] = cvGetWindowHandle(SHOW_WINDOW[i])) != NULL)
	    {
		if (maximize_window)
		{
		    //cvCopy(cameras[i].image, imgshow[i]);
		    imgshow[i] = cameras[i].image_tmp;
		}
		else
		{
		    cvResize(cameras[i].image_tmp, img[i]);//, CV_INTER_NN);
		    imgshow[i] = img[i];
		    
		    if (cameras[0].shutter_control)
			mean[i] = gain_control (imgshow[i], &haarROI[i], &cameras[i], &haar);
		    /*
		    // prueba para ver el retardo en el cambio de shutter
		    if (shuttear++ ==30)
		    {
			//pthread_mutex_lock(&mutex_sync);
			shuttear =0;
			if (cameras[0].shutter_value == 300)
			    cameras[0].shutter_value_new = 1000;
			else 
			    cameras[0].shutter_value_new = 300;
			//dc_set_feature(&cameras[0], 0, FEATURE_SHUTTER, (int)cameras[0].shutter_value); 
			cameras[1].shutter_value_new = cameras[0].shutter_value_new;
			//pthread_mutex_unlock(&mutex_sync);
		    }
		    usleep(12000);
		    */
		}
	    }

	}
	usleep(10000);
	if (1)//!veces--)
	{
	    veces =2;
	    gdk_threads_enter();
	    for (i=0; i<numCams; i++)
		cvShowImage(SHOW_WINDOW[i], imgshow[i]);
	    gdk_threads_leave();
	}
	//printf("means: %lf, %lf\n", mean[0], mean[1]);

	//si se han cerrado todas las ventanas de visualización, se acaba el hilo de visualizar
	if (!window[0] && !window[1])
	{
	    gtk_signal_emit_by_name(GTK_OBJECT(window1), "destroy");
	    break;
	}

	do {
	    sched_yield();
	    usleep(23000);
	} while (hold_window);
	//_________________________________________
	if (1)
	{
	    if (max2)
	    {
		cvMoveWindow(SHOW_WINDOW[0], 800, 450);
		cvMoveWindow(SHOW_WINDOW[1], 800, 750);
		max2 = false;
	    }
	    if (maximize_window && !maximize_window_old)
	    {
		cvMoveWindow(SHOW_WINDOW[0],800, 0);
		cvMoveWindow(SHOW_WINDOW[1],800, 550);
		cvDestroyWindow("Enfoque");
		max2 = false;
	    }
	    else if (!maximize_window && maximize_window_old) 
	    {
		cvDestroyWindow("Enfoque");
		max2 = true;
	    }
	    maximize_window_old = maximize_window;
	}
	//_________________________________________
	    
    }
    
    for (i=0; i<numCams; i++)
	cvReleaseImage(&img[i]);
    return NULL;
}

//************************************************************************
//                         
//   GRABACION O COMPRESION de video
// 
//************************************************************************

bool grabar_frame(captura_params_t * captura)
{
    camera_control * camera = captura->camera;
    image_params_t *img_buf;
    IplImage * img_tmp;
    int tryis=0;

    //printf("Grabar **************************************************\n");

    //Escribir parametros en la primera linea de cada frame
    frame_header *head = (frame_header *)camera->image->imageData;

    head->start_code = FRAME_START_CODE;
    head->nframe = camera->written_frame;
    head->version = 2;
    head->counter = 0;
    head->width = camera->image->width;    
    head->height = camera->image->height;
    head->ncameras = captura->cam;
    memcpy(head->color_space, camera->image->colorModel, 4);
    head->fps = camera->frame_rate;
    head->shutter_usec = (unsigned int) (camera->shutter_value*20);
    head->gain = (unsigned int)gain_value;
    head->brightness = (unsigned int) brightness_value;
    head->lighting = (unsigned int)light_intensity_value;
    head->filltime = camera->camera.filltime;
	
    //***************** pone la image en la queue
    //saca un buffer de la cola de disponibles
    pthread_mutex_lock(&captura->mutex_free);
    tryis =0;
    while (captura->queue_free_img.empty())
    {
	pthread_mutex_unlock(&captura->mutex_free);
	printf("Error, no hay buffers disponibles para escritura\n");fflush(stdout);
	if (tryis++ <= 10)
	{
	    return false;
	}
		
	usleep(5000);
	pthread_mutex_lock(&captura->mutex_free);
	    
    }	    
	    
    img_buf = captura->queue_free_img.front();
    captura->queue_free_img.pop();
    pthread_mutex_unlock(&captura->mutex_free);

    //intercambia las imágenes del buffer y de la captura de las cámaras, 
    //  para evitar tener que copiarlas
    img_tmp = camera->image;
    camera->image = img_buf->img;
    img_buf->img = img_tmp;

    //y pone en buffer en la cola de compresión
    pthread_mutex_lock(&captura->mutex_img);
    captura->queue_img.push(img_buf);
    //captura->queue_free_img.push(img_buf);
    pthread_mutex_unlock(&captura->mutex_img);
	
    //usleep(2000);

    return true;
}


/**************************
/ función para inicializar los hilos del compresión y escritura en disco de los videos
*/
void init_captura_and_save(captura_params_t *captura, int buffer_img_size)
{
    int i, j;
    image_params_t * img_buf;

    //se crean los buffer donde guardar las imágenes
    for (i=0; i<captura[0].camera->numCams; i++)
    {
	captura[i].readframes = 0;
	gettimeofday(&captura[i].lastfilltime, NULL);
	captura[i].quit = captura[i].cerrar_video = false;
	captura[i].buffer_img_size = buffer_img_size;

	for (j=0; j<buffer_img_size; j++)
	{
	    //da memoria a un nuevo buffer de imagen
	    img_buf = new image_params_t;
	    img_buf->img = cvCreateImage(cvGetSize(captura[i].camera->image), IPL_DEPTH_8U, 1);
	    //y lo mete ne la queue de buffers libras
	    captura[i].queue_free_img.push(img_buf);
	}
	
	//inicialización de los semáforos
	pthread_mutex_init(&captura[i].mutex_img, NULL);
	pthread_mutex_init(&captura[i].mutex_free, NULL);
	
	//indicación de la cámara a usar
	captura[i].cam = i;

	//crea el video 
	captura[i].fourcc = CV_FOURCC('F', 'F', 'V', '1');
	//captura[i].video = cvCreateVideoWriter(captura[i].filename, captura[i].fourcc, 30, cvGetSize(captura[i].camera->image), 1);
	//lanza el hilo de compresión para cada cámara
	//printf("Thread_Create( Thread_Func_Compression_Thread...);\n");
	//pthread_create(&captura[i].thread, NULL, pthread_func_compression_thread, &captura[i]); 
	captura[i].video=0;
	captura[i].file=0;
    }
}

/****/
//liberación de toda la memoria asignada
void finish_captura_and_save(captura_params_t * captura)
{
    int i;
    image_params_t *img_buf;
    grabar_flag = false;

    //primero espera a que acaben los hilos
    for (i=0; i<captura[0].camera[0].numCams; i++)
    {
	finalizar_grabacion(&captura[i]);
    }
    for (i=0; i<captura[0].camera[0].numCams; i++)
    {
	if (captura[i].thread)
	{
	    pthread_join(captura[i].thread, NULL);
	    printf("Fin hilo grabación\n");
	}
    }
    
    //libera toda la memoria creada para los buffer de las imágenes
    for (i=0; i<captura[0].camera[0].numCams; i++)
    {
	pthread_mutex_lock(&captura[i].mutex_free);
	while (!captura[i].queue_free_img.empty())
	{
	    img_buf = captura[i].queue_free_img.front();
	    captura[i].queue_free_img.pop();
	    cvReleaseImage(&img_buf->img);
	    delete []img_buf;
	    captura->video=0;
	    captura[i].video=0;
	    captura[i].file=0;
	}
	pthread_mutex_unlock(&captura[i].mutex_free);
	pthread_mutex_lock(&captura[i].mutex_img);
	while (!captura[i].queue_img.empty())
	{
	    img_buf = captura[i].queue_img.front();
	    captura[i].queue_img.pop();
	    cvReleaseImage(&img_buf->img);
	    delete []img_buf;
	    captura->video=0;
	    captura[i].video=0;
	    captura[i].file=0;
	}
	pthread_mutex_unlock(&captura[i].mutex_img);

	pthread_mutex_destroy(&captura[i].mutex_img);
	pthread_mutex_destroy(&captura[i].mutex_free);
    }

}



/**************************************
/ hilo que se encarga de la compresión del video y almacenarlo
*/ 
void * pthread_func_compression_thread(void * params)
{
    captura_params_t *captura = (captura_params_t *)params;
    int j;
    IplImage *imgcolor;
    image_params_t *img_buf;

    imgcolor = cvCreateImage(cvGetSize(captura->camera->image), IPL_DEPTH_8U, 3);

    while (1)
    {
	//junta tres frames en 1;
	for (j=0; j<3; j++)
	{
	    pthread_mutex_lock(&captura->mutex_img);
	    //espera a que haya imágenes disponibles para comprimir
	    while(captura->queue_img.empty())
	    {
		//libera el video
		if (captura->quit)
		{
		    if (captura->video)
		    {
			cvReleaseVideoWriter(&captura->video);
			printf("\ncerrando video %d en %s\n", captura->cam, captura->filename);
			captura->video=0;

			//cambia los permisos para el archivo
			if (getuid()==0)
			{
			    char buffer[1000];
			    sprintf(buffer, "chown %s %s", strrchr(getenv("HOME"), '/')+1, 
				    captura->filename);
			    system(buffer);
			    sprintf(buffer, "chgrp userdir %s", captura->filename);
			    system(buffer);
			}
			pthread_mutex_unlock(&captura->mutex_img);
		    }
		    cvReleaseImage(&imgcolor);
		    pthread_exit(0);
		    return NULL;
		}
		pthread_mutex_unlock(&captura->mutex_img);
		usleep(TIME_SLEEP_COMPRESOR_USEC);
		pthread_mutex_lock(&captura->mutex_img);
	    }
	    //ya hay imágenes para comprimir
	    //extrae la primera imagen lista para comprimir
	    img_buf = captura->queue_img.front();
	    captura->queue_img.pop(); //y elimina la imagen del buffer
	    pthread_mutex_unlock(&captura->mutex_img);

	    //copia la imagen capturada a la imagen en color para comprimir
	    memcpy(imgcolor->imageData + j*img_buf->img->imageSize, img_buf->img->imageData, 
		   img_buf->img->imageSize);

	}
	
	//cada tres imágenes, las mete en una imagen en color y las comprime
	cvWriteFrame(captura->video, imgcolor);
	sched_yield();
    }
}


void vaciar_buffers(captura_params_t * captura)
{
    image_params_t *img_buf;

    pthread_mutex_lock(&captura->mutex_img);
    pthread_mutex_lock(&captura->mutex_free);
    while(!captura->queue_img.empty())
    {
	//extrae la primera imagen lista para comprimir
	img_buf = captura->queue_img.front();
	captura->queue_img.pop(); //y elimina la imagen del buffer

	//cuando acaba con el buffer de la imagen, lo pone en la cola de buffers libres
	captura->queue_free_img.push(img_buf);
    }
    pthread_mutex_unlock(&captura->mutex_free);
    pthread_mutex_unlock(&captura->mutex_img);
    
}

/**************************************
/ hilo que se encarga de la compresión del video y almacenarlo
*/ 
void * pthread_func_storing_thread(void * params)
{
    captura_params_t *captura = (captura_params_t *)params;
    image_params_t *img_buf;

    struct timeval t1;
    gettimeofday(&t1, NULL);
    srand(t1.tv_usec);
    qgrabar_camara = captura->cam;

    while (1)//captura->quit)
    {
	pthread_mutex_lock(&captura->mutex_img);
	//espera a que haya imágenes disponibles para comprimir
	while(captura->queue_img.empty())
	{
	    if (captura->quit)
	    {
		break;
	    }
	    pthread_mutex_unlock(&captura->mutex_img);
	    usleep(TIME_SLEEP_COMPRESOR_USEC*(rand()%4));
	    pthread_mutex_lock(&captura->mutex_img);
	}
	if (captura->quit)
	    break;

	if (qgrabar_camara != captura->cam)
	{
	    pthread_mutex_unlock(&captura->mutex_img);
	    while (50 < (captura->queue_img.size()*100/captura->buffer_img_size));
	    {
		usleep(TIME_SLEEP_COMPRESOR_USEC*((rand()%4)+1));
	    }
	    //comienza a grabar
	    qgrabar_camara = captura->cam;
	    pthread_mutex_lock(&captura->mutex_img);
	}

	//ya hay imágenes para comprimir
	//extrae la primera imagen lista para comprimir
	img_buf = captura->queue_img.front();
	captura->queue_img.pop(); //y elimina la imagen del buffer
	pthread_mutex_unlock(&captura->mutex_img);

	//almacena la imagen capturada
	if (captura->file)
	{
	    fwrite (img_buf->img->imageData, 		    
		    sizeof(char), img_buf->img->imageSize, 		    
		    captura->file);
	}
	else 
	{
	    printf("*********************************\n");
	    printf("Error grave en el fichero de escritura.\n");
	}

	//cuando acaba con el buffer de la imagen, lo pone en la cola de buffers libres
	pthread_mutex_lock(&captura->mutex_free);
	captura->queue_free_img.push(img_buf);
	pthread_mutex_unlock(&captura->mutex_free);

	sched_yield();
    }

    // fin de la grabación
    //hay que liberar todos los buffers
    if (captura->file)
    {
	//libera el video
	printf("\ncerrando video %d en %s\n", captura->cam, captura->filename);
	
	fclose(captura->file);
	captura->file =0;
	//cambia los permisos para el archivo
	if (getuid()==0)
	{
	    char buffer[1000];
	    sprintf(buffer, "chown %s %s", strrchr(getenv("HOME"), '/')+1, 
		    captura->filename);
	    system(buffer);
	    sprintf(buffer, "chgrp admin %s", captura->filename);
	    system(buffer);
	}
	pthread_mutex_unlock(&captura->mutex_img);
    }
    fflush(stdout);
    return NULL;
}



void iniciar_compresion(captura_params_t * captura, const char *filename)
{
    strcpy(captura->filename, filename);
    captura->video = cvCreateVideoWriter(captura->filename, captura->fourcc, 
					 30, cvGetSize(captura->camera->image), 1);
    captura->file = 0;
    captura->quit = false;

    //lanza el hilo de compresión para cada cámara
    printf("Thread_Create( Thread_Func_Compression_Thread ...);\n");
    pthread_create(&captura->thread, NULL, pthread_func_compression_thread, captura); 

    
    if (getuid() == 0)     //SCHED_OTHER, SCHED_FIFO, or SCHED_RR
	change_sched(captura->thread, SCHED_RR, 1);
}

void iniciar_grabacion(captura_params_t * captura, const char *filename)
{ 
    strcpy(captura->filename, filename);
    captura->file = fopen(captura->filename, "w");
    captura->video = 0;
    captura->quit = false;

    //vacía los buffers
    vaciar_buffers(captura);

    //lanza el hilo de compresión para cada cámara
    printf("Thread_Create( Thread_Func_Storing_Thread ...);\n");
    pthread_create(&captura->thread, NULL, pthread_func_storing_thread, captura); 
    
    if (getuid() == 0)     //SCHED_OTHER, SCHED_FIFO, or SCHED_RR
	change_sched(captura->thread, SCHED_RR, 1);
}

void finalizar_grabacion(captura_params_t * captura)
{
    captura->quit = true;
}
